package wiki.xsx.core.handler;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StringUtils;

import java.util.Properties;

/**
 * 数据库助手
 * @author xsx
 * @date 2019/4/25
 * @since 1.8
 */
@Slf4j
public class DBHandler {
    /**
     * 对象模板
     */
    private RedisTemplate<String, Object> redisTemplate;
    /**
     * 字符串模板
     */
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 数据库助手构造
     * @param redisTemplate 对象模板
     * @param stringRedisTemplate 字符串模板
     */
    private DBHandler(RedisTemplate<String, Object> redisTemplate, StringRedisTemplate stringRedisTemplate) {
        this.redisTemplate = redisTemplate;
        this.stringRedisTemplate = stringRedisTemplate;
    }

    /**
     * 获取实例
     * @param redisTemplate 对象模板
     * @param stringRedisTemplate 字符串模板
     * @return 返回实例
     */
    public static DBHandler getInstance(RedisTemplate<String, Object> redisTemplate, StringRedisTemplate stringRedisTemplate) {
        return new DBHandler(redisTemplate, stringRedisTemplate);
    }

    /**
     * 获取当前数据库索引
     * @return 返回当前数据库索引
     */
    public int getDB() {
        int index = 0;
        RedisConnectionFactory redisConnectionFactory;
        try {
            redisConnectionFactory = this.redisTemplate.getRequiredConnectionFactory();
        }catch (IllegalStateException ex) {
            redisConnectionFactory = this.stringRedisTemplate.getRequiredConnectionFactory();
        }
        if (redisConnectionFactory instanceof LettuceConnectionFactory) {
            LettuceConnectionFactory factory = (LettuceConnectionFactory) redisConnectionFactory;
            index = factory.getDatabase();
        }else if (redisConnectionFactory instanceof JedisConnectionFactory) {
            JedisConnectionFactory factory = (JedisConnectionFactory) redisConnectionFactory;
            if (factory.isRedisSentinelAware()) {
                RedisSentinelConfiguration sentinelConfiguration = factory.getSentinelConfiguration();
                if (sentinelConfiguration != null) {
                    index = sentinelConfiguration.getDatabase();
                }else{
                    log.error("sentinelConfiguration is null, get index of db is error, will returned '0'");
                }
            }else{
                RedisStandaloneConfiguration standaloneConfiguration = factory.getStandaloneConfiguration();
                if (standaloneConfiguration!=null) {
                    index = standaloneConfiguration.getDatabase();
                    log.error("'standaloneConfiguration' is null, get index of db is error, will returned '0'");
                }
            }
        }else {
            throw new RuntimeException("no support connection factory");
        }
        return index;
    }

    /**
     * 设置当前数据库索引
     * @since redis 1.0.0
     * @param dbIndex 数据库索引
     */
    public void setDB(int dbIndex) {
        this.redisTemplate.setConnectionFactory(CommonHandler.getConnectionFactory(dbIndex));
        this.stringRedisTemplate.setConnectionFactory(CommonHandler.getConnectionFactory(dbIndex));
    }

    /**
     * 获取原有数据库索引并设置新索引
     * @since redis 1.0.0
     * @param dbIndex 数据库索引
     * @return 返回原有索引
     */
    public int getAndSetDB(int dbIndex) {
        int index = getDB();
        setDB(dbIndex);
        return index;
    }

    /**
     * 清理当前数据库
     * <p>FLUSHDB</p>
     * @since redis 1.0.0
     */
    public void clearDB() {
        try {
            this.redisTemplate.getRequiredConnectionFactory().getConnection().serverCommands().flushDb();
        }catch (IllegalStateException ex) {
            try {
                this.stringRedisTemplate.getRequiredConnectionFactory().getConnection().serverCommands().flushDb();
            }catch (IllegalStateException e) {
                log.error(e.getMessage());
            }
        }
    }

    /**
     * 清理所有数据库
     * <p>FLUSHALL</p>
     * @since redis 1.0.0
     */
    public void clearDBAll() {
        try {
            this.redisTemplate.getRequiredConnectionFactory().getConnection().serverCommands().flushAll();
        }catch (IllegalStateException ex) {
            try {
                this.stringRedisTemplate.getRequiredConnectionFactory().getConnection().serverCommands().flushAll();
            }catch (IllegalStateException e) {
                log.error(e.getMessage());
            }
        }
    }

    /**
     * 获取当前数据库信息
     * <p>INFO [default]</p>
     * @since redis 1.0.0
     * @return 返回当前数据库信息
     */
    public Properties getDBInfo() {
        return this.stringRedisTemplate.getRequiredConnectionFactory().getConnection().serverCommands().info();
    }

    /**
     * 获取当前数据库信息
     * <p>INFO [section]</p>
     * @since redis 1.0.0
     * @param option 选项
     * <p>server: Redis服务器的一般信息</p>
     * <p>clients: 客户端的连接部分</p>
     * <p>memory: 内存消耗相关信息</p>
     * <p>persistence: RDB和AOF相关信息</p>
     * <p>stats: 一般统计</p>
     * <p>replication: 主/从复制信息</p>
     * <p>cpu: 统计CPU的消耗</p>
     * <p>commandstats: Redis命令统计</p>
     * <p>cluster: Redis集群信息</p>
     * <p>keyspace: 数据库的相关统计</p>
     * <p>all: 所有信息</p>
     * <p>default: 默认设置的信息</p>
     * @return
     */
    public Properties getDBInfo(String option) {
        if (StringUtils.hasText(option)) {
            return this.stringRedisTemplate.getRequiredConnectionFactory().getConnection().serverCommands().info(option);
        }
        return this.getDBInfo();
    }

    /**
     * 获取spring redis模板
     * @return 返回对象模板
     */
    public RedisTemplate getRedisTemplate() {
        return this.redisTemplate;
    }

    /**
     * 获取spring string redis模板
     * @return 返回字符串模板
     */
    public StringRedisTemplate getStringRedisTemplate() {
        return this.stringRedisTemplate;
    }
}
